﻿using System;
using System.Collections.Generic;
using System.Linq;
using FNN.CustomFunctions;

namespace FNN.NetworkComponents
{
    [Serializable]
    public class Neuron
    {
        private double _inputValue;
        private IFunction _neuronFunction;
        private FunctionTypes _neuronFunctionId;
        private int _neuronId;

        /// <summary>
        ///     Конструктор нейрона на основе функции
        /// </summary>
        /// <param name="functionParam">Функция, которую помещаем в нейрон</param>
        public Neuron(FunctionTypes functionParam)
        {
            if (functionParam == FunctionTypes.NotDefined)
            {
                var randomizer = new Random();
                var newType = randomizer.Next(1, 6);
                functionParam = (FunctionTypes) newType;
            }

            switch (functionParam)
            {
                case FunctionTypes.Linear:
                    _neuronFunction = new Linear();
                    break;
                case FunctionTypes.Sigmoid:
                    _neuronFunction = new Sigmoid();
                    break;
                case FunctionTypes.SimpleFractional:
                    _neuronFunction = new SimpleFractional();
                    break;
                case FunctionTypes.Sinus:
                    _neuronFunction = new Sinus();
                    break;
                case FunctionTypes.HyperboleTangent:
                    _neuronFunction = new HyperboleTangent();
                    break;
                case FunctionTypes.BipolarSigmoid:
                    _neuronFunction = new BipolarSigmoid();
                    break;
            }
        }

        /// <summary>
        ///     Конструктор по умолчанию
        /// </summary>
        public Neuron()
        {
            _neuronFunction = new Linear();
        }

        /// <summary>
        ///     Конструктор копирования
        /// </summary>
        /// <param name="source">Исходный экземпляр</param>
        public Neuron(Neuron source)
        {
            _neuronId = source._neuronId;
            _inputValue = source._inputValue;

            switch (source._neuronFunctionId)
            {
                case FunctionTypes.Linear:
                    _neuronFunction = new Linear();
                    break;
                case FunctionTypes.Sigmoid:
                    _neuronFunction = new Sigmoid();
                    break;
                case FunctionTypes.SimpleFractional:
                    _neuronFunction = new SimpleFractional();
                    break;
                case FunctionTypes.Sinus:
                    _neuronFunction = new Sinus();
                    break;
                case FunctionTypes.HyperboleTangent:
                    _neuronFunction = new HyperboleTangent();
                    break;
                case FunctionTypes.BipolarSigmoid:
                    _neuronFunction = new BipolarSigmoid();
                    break;
            }
        }

        /// <summary>
        ///     Правило нейрона
        /// </summary>
        public IFunction NeuronFunction
        {
            get { return _neuronFunction; }
            set { _neuronFunction = value; }
        }

        /// <summary>
        ///     Входное значение нейрона
        /// </summary>
        public double InputValue
        {
            get { return _inputValue; }
            set { _inputValue = value; }
        }

        /// <summary>
        /// Идентификатор нейрона
        /// </summary>
        public int NeuronId
        {
            get { return _neuronId; }
            set { _neuronId = value; }
        }

        /// <summary>
        /// Идентификатор активационной функции
        /// </summary>
        public FunctionTypes NeuronFunctionId
        {
            get { return _neuronFunctionId; }
            set { _neuronFunctionId = value; }
        }

        /// <summary>
        ///     Получить результат нейрона с передачей параметра
        /// </summary>
        /// <returns>Десятичное число от 0 до 1</returns>
        public double GetResult()
        {
            return _neuronFunction.Calculate(_inputValue);
        }

        /// <summary>
        ///     Расчет входного значения
        /// </summary>
        /// <param name="synapsesMap"></param>
        public void CalculateInput(List<Synapse> synapsesMap)
        {
            var targetSynapses = synapsesMap.Where(o => o.OutputNeuron == this).ToList();
            var result = targetSynapses.Sum(synapse => synapse.InputNeuron.GetResult()*synapse.Weight);
            _inputValue = result;
        }
    }
}